package game.net;

import game.Game;
import game.entities.PlayerMP;
import game.net.packets.Packet;
import game.net.packets.Packet.PacketTypes;
import game.net.packets.Packet00Login;
import game.net.packets.Packet01Disconnect;
import game.net.packets.Packet02Move;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

public class GameServer extends Thread{
	
	private DatagramSocket socket;
	private Game game;
	private List<PlayerMP> connectedPlayers = new ArrayList<PlayerMP>();
	
	public GameServer(Game game){
		this.game = game;
		try {
			this.socket = new DatagramSocket(1331);
		} catch (SocketException e) {
			e.printStackTrace();
		}
	}
	
	public void run(){
		while(true){
			byte[] data = new byte[1024];
			DatagramPacket packet = new DatagramPacket(data, data.length);
			try {
				socket.receive(packet);
			} catch (IOException e) {
				e.printStackTrace();
			}
			this.parsePacket(packet.getData(),packet.getAddress(),packet.getPort());
			String message = new String(packet.getData()).trim();
			//System.out.println("CLIENT [" + packet.getAddress().getHostAddress() + ":" + packet.getPort() + "]> " +message);
			if(message.trim().equalsIgnoreCase("ping")){
				sendData("pong".trim().getBytes(),packet.getAddress(),packet.getPort());	
			}
		}
	}
	
	private void parsePacket(byte[] data, InetAddress address, int port) {
		String message = new String(data).trim();
		PacketTypes type = Packet.lookupPacket(new String(message.substring(0,2)));
		Packet packet = null;
		switch(type){
		default:
		case INVALID:
			break;
		case LOGIN:
			packet = new Packet00Login(data);
			System.out.println("["+address.getHostAddress()+":"+port+"] " + ((Packet00Login)packet).getUsername()+" has connected...");
			PlayerMP player = new PlayerMP(game.level,100,100,((Packet00Login)packet).getUsername(),address,port);
			this.addConnection(player,((Packet00Login)packet));
			break;
		case DISCONNECT:
			packet = new Packet01Disconnect(data);
			System.out.println("["+address.getHostAddress()+":"+port+"] " + ((Packet01Disconnect)packet).getUsername()+" has left...");
			this.removeConnection(((Packet01Disconnect)packet));
			break;
		case MOVE:
			packet = new Packet02Move(data);
			this.handleMove(((Packet02Move)packet));
			break;
		}
	}
	


	public void addConnection(PlayerMP player, Packet00Login packet){
		boolean alreadyConnected = false;
		for(PlayerMP p : this.connectedPlayers){
			if(player.getUsername().equalsIgnoreCase(p.getUsername())){
				if(p.ipAddress == null){
					p.ipAddress = player.ipAddress;
				}
				if(p.port == -1){
					p.port = player.port;
				}
				alreadyConnected = true;
			} else {
				// relay to the current connected player that there is a new player
				sendData(packet.getData(),p.ipAddress,p.port);
				
				// relay to the new player that the currently connected player exists
				packet = new Packet00Login(p.getUsername(),p.x,p.y);
				sendData(packet.getData(),player.ipAddress,player.port);
			}
		}
		if(!alreadyConnected){
			this.connectedPlayers.add(player);
		}
	}

	private void removeConnection(Packet01Disconnect packet) {
		//PlayerMP player = getPlayerMP(packet.getUsername());
		this.connectedPlayers.remove(getPlayerMPIndex(packet.getUsername()));
		packet.writeData(this);
	}
	
	public PlayerMP getPlayerMP(String username){
		for(PlayerMP player : this.connectedPlayers){
			if(player.getUsername().equals(username)){
				return player;
			}
		}
		return null;
	}
	
	public int getPlayerMPIndex(String username){
		int index = 0;
		for(PlayerMP player : this.connectedPlayers){
			if(player.getUsername().equals(username)){
				break;
			}
			index++;
		}
		return index;
	}
	
	public void sendData(byte[] data, InetAddress ipAddress, int port){
		DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress,port);
		try {
			socket.send(packet);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void sendDataTOAllClients(byte[] data) {
		for(PlayerMP p : connectedPlayers){
			sendData(data,p.ipAddress,p.port);
		}
	}
	
	private void handleMove(Packet02Move packet){
		if(getPlayerMP(packet.getUsername())!=null){
			int index = getPlayerMPIndex(packet.getUsername());
			PlayerMP player = this.connectedPlayers.get(index);
			player.x = packet.getX();
			player.y = packet.getY();
			player.setMoving(packet.isMoving());
			player.setMovingDir(packet.getMovingDir());
			player.setNumSteps(packet.getNumSteps());
			packet.writeData(this);
		}
	}
}
